package top.superflow.core.utils

import java.lang.reflect.Field

import org.apache.commons.lang3.StringUtils
import top.superflow.exception.RequestUnsupportedException
import top.superflow.flow.annotation.Resource
import top.superflow.lang.ArrayUtils
import top.superflow.log.Loggable
import top.superflow.utils.ClassUtils

import scala.collection.mutable.Map

object PojoUtils extends Loggable{

  val GETTER_START = "get"

  def collectGetterToMap(pojo : Any) : Map[String, Any] = {
    val map = Map[String, Any]()
    if(pojo == null || pojo == None) {
      return map
    }

    for(field <- pojo.getClass().getDeclaredFields){
      if(field.getName.startsWith(GETTER_START) && field.getName.size > 3){
        val fieldNameArray = field.getName.replace("get", StringUtils.EMPTY).toCharArray()
        fieldNameArray.update(0,fieldNameArray.apply(0).toLower)
        map += (fieldNameArray.toString() -> field.get(pojo))
      }
    }

    map
  }

  def createInstance(fullClassPath : String) : Any = {
    var clz : Class[_] = ClassUtils.getClass(fullClassPath)

    clz.newInstance()
  }

  def getField(target : Any, name : String): Field = {
    if(target == null){
      logger.warn("Trying to get the field from null object!")
      return null
    }

    if(StringUtils.isBlank(name)){
      logger.warn("Trying to get the field by a blank name!")
      return null
    }

    val targetClass = target.getClass
    val fieldByName = targetClass.getDeclaredField(name)
    if(fieldByName != null){
      return fieldByName
    }

    for(field <- targetClass.getDeclaredFields){
      val resourceAnnotation = field.getAnnotation(classOf[Resource])
      val aliases = resourceAnnotation.alias()

      if(containAlias(aliases, name)){
        return field;
      }
    }

    null
  }

  private def containAlias(aliases : Array[String], targetAlias : String) : Boolean = {
    if(ArrayUtils.isEmpty(aliases)){
      return false
    }

    return aliases.contains(targetAlias)
  }

  def setField(field : Field, target:Any, value : Any): Unit ={
    if(field == null){
      logger.warn("Trying to set value to the null field!")
      return
    }

    val isAccessible = field.isAccessible
    field.setAccessible(true)
    field.set(target, value)
    field.setAccessible(isAccessible)
  }

  def copyTo[T](s : T, t : T): Unit ={
    if(s == null){
      return;
    }

    if(t == null){
      throw new RequestUnsupportedException("It's not supported to copy an object to null object!")
    }

    for(sourceField <- s.getClass.getDeclaredFields){
      val sourceFieldAccessible = sourceField.isAccessible

      sourceField.setAccessible(true)

      val sourceFieldValue = sourceField.get(s)

      sourceField.set(t, sourceFieldValue)

      sourceField.setAccessible(sourceFieldAccessible)

      t
    }


  }

  /**
    * Note, this is not a deep clone
    * @param s
    * @tparam T
    * @return
    */
  def clone[T] (s : T) :T = {
    if(s == null){
      throw new RequestUnsupportedException("It's meaningless to clone null!")
    }

    val t = s.getClass.newInstance()

    for(sourceField <- s.getClass.getDeclaredFields){
      val sourceFieldAccessible = sourceField.isAccessible

      sourceField.setAccessible(true)

      val sourceFieldValue = sourceField.get(s)

      sourceField.set(t, sourceFieldValue)

      sourceField.setAccessible(sourceFieldAccessible)

      t
    }

    t
  }
}
